package product

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/service/backend/common/errorx"
	"mall/service/backend/common/i18n"
	"mall/service/product/model"
	"mall/service/product/rpc/internal/svc"
	"mall/service/product/rpc/internal/utils/errorhandler"
	"mall/service/product/rpc/types/product"

	"github.com/gosimple/slug"
	"github.com/volatiletech/null/v8"
	"github.com/zeromicro/go-zero/core/logx"
)

type UpdateProductLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewUpdateProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateProductLogic {
	return &UpdateProductLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *UpdateProductLogic) UpdateProduct(in *product.ProductInfo) (*product.BaseResp, error) {
	// logx.Infof("UpdateProduct : %+v ", in)

	tx, err := l.svcCtx.DB.BeginTx(l.ctx, nil)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if len(in.Ids) > 0 { // 批量修改状态
		now := time.Now()
		for _, id := range in.Ids {
			if id > 0 && in.Status != nil {
				goods, err := l.svcCtx.CurrentProduct.FindOneNoCache(l.ctx, tx, id)
				if err != nil {
					tx.Rollback()
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				goods.Status = uint16(*in.Status)
				goods.ProductModifiedDate = now
				err = l.svcCtx.CurrentProduct.Update(l.ctx, tx, goods, nil)
				if err != nil {
					tx.Rollback()
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}
			} else if id > 0 && in.ShowStock != nil && *in.ShowStock > 0 {
				err = l.svcCtx.CurrentProduct.UpdateMetum(l.ctx, tx, &model.ProductMetum{
					ProductID: id,
					MetaKey:   "_sync",
					MetaValue: null.StringFrom("1"),
				})
				if err != nil {
					tx.Rollback()
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}
			}
		}

		tx.Commit()

		return &product.BaseResp{
			Msg: i18n.UpdateSuccess,
		}, nil
	}

	if in.Id == nil || *in.Id == 0 {
		return nil, errorx.NewInvalidArgumentError(i18n.ValidationError)
	}

	if in.Title != nil && len(strings.TrimSpace(*in.Title)) == 0 {
		return nil, errorx.NewInvalidArgumentError("product.TitleRequired")
	}

	if in.ProductCode != nil && len(strings.TrimSpace(*in.ProductCode)) == 0 {
		return nil, errorx.NewInvalidArgumentError("product.codeRequired")
	}

	id := *in.Id
	goods, err := l.svcCtx.CurrentProduct.FindOneNoCache(l.ctx, tx, id)
	if err != nil {
		tx.Rollback()
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if (in.Title != nil && goods.Title != strings.TrimSpace(*in.Title)) || (in.Slug != nil && goods.Slug != strings.TrimSpace(*in.Slug)) { // title或slug改变了
		filterMap := make(map[string]map[string][]any)
		if in.CategoryId != nil {
			filterMap[model.ProductProductColumns.CategoryID] = map[string][]any{
				"equalTo": {*in.CategoryId},
			}
		} else {
			filterMap[model.ProductProductColumns.CategoryID] = map[string][]any{
				"equalTo": {goods.CategoryID},
			}
		}
		if in.Title != nil {
			filterMap[model.ProductProductColumns.Title] = map[string][]any{
				"equalTo": {strings.TrimSpace(*in.Title)},
			}
		} else {
			filterMap[model.ProductProductColumns.Title] = map[string][]any{
				"equalTo": {goods.Title},
			}
		}
		if in.Slug != nil && len(strings.TrimSpace(*in.Slug)) > 0 {
			filterMap[model.ProductProductColumns.Slug] = map[string][]any{
				"equalTo": {strings.TrimSpace(*in.Slug)},
			}
		} else {
			filterMap[model.ProductProductColumns.Slug] = map[string][]any{
				"equalTo": {goods.Slug},
			}
		}
		list, _, err := l.svcCtx.CurrentProduct.GetList(l.ctx, model.M{
			"pageSize": uint64(2),
			"select":   ",",
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 1 {
			return nil, errorx.NewInvalidArgumentError("product.titleAlreadyExists")
		}

		if len(list) == 1 && list[0].ID != id {
			return nil, errorx.NewInvalidArgumentError("product.titleAlreadyExists")
		}
	}

	var skipMeta bool
	if in.SkipMeta != nil && *in.SkipMeta > 0 {
		skipMeta = true
	}
	var skipMedia bool
	if in.SkipMedia != nil && *in.SkipMedia > 0 {
		skipMedia = true
	}
	var skipCategory bool
	if in.SkipCategory != nil && *in.SkipCategory > 0 {
		skipCategory = true
	}
	var skipTag bool
	if in.SkipTag != nil && *in.SkipTag > 0 {
		skipTag = true
	}
	var skipEntry bool
	if in.SkipEntry != nil && *in.SkipEntry > 0 {
		skipEntry = true
	}
	var skipContent bool
	if in.SkipContent != nil && *in.SkipContent > 0 {
		skipContent = true
	}
	var skipAttr bool
	if in.SkipAttr != nil && *in.SkipAttr > 0 {
		skipAttr = true
	}
	var skipSpec bool
	if in.SkipSpec != nil && *in.SkipSpec > 0 {
		skipSpec = true
	}
	var skipSku bool
	if in.SkipSku != nil && *in.SkipSku > 0 {
		skipSku = true
	}
	var skipTracing bool
	if in.SkipTracing != nil && *in.SkipTracing > 0 {
		skipTracing = true
	}

	if in.Title != nil {
		goods.Title = strings.TrimSpace(*in.Title)
	}
	if in.ProductCode != nil {
		goods.ProductCode = strings.TrimSpace(*in.ProductCode)
	}
	var text string
	if in.Slug != nil && goods.Slug != strings.TrimSpace(*in.Slug) { // slug改变了
		goods.Slug = strings.TrimSpace(*in.Slug)
		if len(goods.Slug) > 0 {
			slug.Lowercase = false
			text = slug.Make(goods.Slug)
		}
	}
	if in.Excerpt != nil {
		goods.Excerpt = strings.TrimSpace(*in.Excerpt)
	}

	var visibility uint32
	if in.Visibility != nil && *in.Visibility > 0 {
		visibility = *in.Visibility
	} else {
		visibility = globalkey.PostVisibilityPublic
	}

	var publicDate time.Time
	var isFuturePublicDate bool
	if in.PublicDate != nil && len(*in.PublicDate) > 0 {
		if localTime, err := modelx.ParseLocalTime(*in.PublicDate); err == nil {
			publicDate = localTime
			isFuturePublicDate = true
		}
	}

	var status uint16
	var pendingStatus int32 // 审核后的状态
	if in.Status != nil {
		if *in.Status == uint32(globalkey.PostStatusPublish) { // 发布
			if isFuturePublicDate { // 未来发布
				status = uint16(globalkey.PostStatusFuture) // 暂为future状态
			} else { // 立即发布
				if visibility == globalkey.PostVisibilityPrivate {
					status = uint16(globalkey.PostStatusPrivate)
				} else {
					status = uint16(globalkey.PostStatusPublish)
				}
			}
		} else { // 其他状态
			status = uint16(*in.Status)
			if isFuturePublicDate { // 未来发布
				pendingStatus = globalkey.PostStatusFuture // 审核后为future状态
			} else { // 立即发布
				if visibility == globalkey.PostVisibilityPrivate {
					pendingStatus = globalkey.PostStatusPrivate
				} else {
					pendingStatus = globalkey.PostStatusPublish
				}
			}
		}
		goods.Status = status
	}

	if in.Sort != nil {
		goods.SortOrder = int(*in.Sort)
	}
	if in.CategoryId != nil {
		goods.CategoryID = *in.CategoryId
	}
	if in.ParentId != nil {
		goods.ParentID = *in.ParentId
	}
	goods.ProductModifiedDate = time.Now()

	if isFuturePublicDate {
		goods.ProductDate = publicDate
	}
	if len(text) > 0 {
		goods.GUID = "/product/" + text
	}
	var sticky string
	if in.Sticky != nil && *in.Sticky > 0 {
		switch int32(*in.Sticky) {
		case globalkey.BlogStickyTypeCategory:
			sticky = "category"
		case globalkey.BlogStickyTypeTag:
			sticky = "tag"
		default:
			sticky = "frontpage"
		}
	}

	rel := &model.ProductRel{}
	if !skipContent || !skipTracing {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductDetailColumns.ProductID] = map[string][]any{
			"equalTo": {id},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetDetailList(l.ctx, model.M{
			"pageSize": uint64(1),
			"orderBy":  model.ProductDetailColumns.DetailID + " DESC",
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			rel.Detail = list[0]
			if !skipContent {
				if in.Content != nil {
					rel.Detail.Content = *in.Content
				}
				if in.ContentFiltered != nil {
					rel.Detail.ContentFiltered = *in.ContentFiltered
				}
			}
			if !skipTracing {
				if in.Tracing != nil {
					rel.Detail.Tracing = null.StringFrom(*in.Tracing)
				}
				if in.TracingFiltered != nil {
					rel.Detail.TracingFiltered = null.StringFrom(*in.TracingFiltered)
				}
			}
		}
	}

	var thumbnailIdMetaId uint64
	var thumbnailUrlMetaId uint64
	var photoCountMetaId uint64
	var pendingStatusMetaId uint64
	// var viewCountMetaId uint64
	// var commentCountMetaId uint64
	// var forwardCountMetaId uint64
	var stickyMetaId uint64
	var gemPriceMinMetaId uint64
	var gemPriceMaxMetaId uint64
	var retailPriceMinMetaId uint64
	var retailPriceMaxMetaId uint64
	// var stockMetaId uint64
	var preSaleCountMetaId uint64
	// var totalSaleCountMetaId uint64
	// var isHotMetaId uint64
	var sellPointMetaId uint64
	var stockUnitMetaId uint64
	var showStockMetaId uint64
	if !skipMeta {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductMetumColumns.ProductID] = map[string][]any{
			"equalTo": {id},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetMeta(l.ctx, model.M{
			"orderBy": model.ProductMetumColumns.MetaID + " DESC",
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 { // 原有的 Meta（决定现有的是增还是改）
			var omids []uint64
			oMetaMap := make(map[uint64]*model.ProductMetum)
			keyMap := make(map[string]struct{})
			for _, v := range list {
				if len(v.MetaKey) > 0 && v.MetaValue.Valid {
					if _, ok := keyMap[v.MetaKey]; ok { // 防重复
						continue
					}

					keyMap[v.MetaKey] = struct{}{}

					switch v.MetaKey { // 先排除特殊Key
					case "_thumbnail_id":
						thumbnailIdMetaId = v.MetaID
					case "_thumbnail_url":
						thumbnailUrlMetaId = v.MetaID
					case "_photo_count":
						photoCountMetaId = v.MetaID
					case "_pending_status":
						pendingStatusMetaId = v.MetaID
					case "_view_count":
						// viewCountMetaId = v.MetaID
					case "_comment_count":
						// commentCountMetaId = v.MetaID
					case "_forward_count":
						// forwardCountMetaId = v.MetaID
					case "_sticky":
						stickyMetaId = v.MetaID
					case "_gem_price_min":
						gemPriceMinMetaId = v.MetaID
					case "_gem_price_max":
						gemPriceMaxMetaId = v.MetaID
					case "_retail_price_min":
						retailPriceMinMetaId = v.MetaID
					case "_retail_price_max":
						retailPriceMaxMetaId = v.MetaID
					case "_stock":
						// stockMetaId = v.MetaID
					case "_pre_sale_count":
						preSaleCountMetaId = v.MetaID
					case "_total_sale_count":
						// totalSaleCountMetaId = v.MetaID
					case "_is_hot":
						// isHotMetaId = v.MetaID
					case "_sell_point":
						sellPointMetaId = v.MetaID
					case "_stock_unit":
						stockUnitMetaId = v.MetaID
					case "_show_stock":
						showStockMetaId = v.MetaID
					case "_review_count", "_rating_count":
						//
					default:
						omids = append(omids, v.MetaID)
						oMetaMap[v.MetaID] = v
					}
				}
			}

			if len(in.Meta) > 0 {
				// 现有的 Meta（决定原有的是删还是留）
				nmidMap := make(map[uint64]struct{})
				for _, v := range in.Meta {
					if v.Key == nil || len(strings.TrimSpace(*v.Key)) == 0 {
						continue
					}

					if v.Value == nil || len(strings.TrimSpace(*v.Value)) == 0 {
						continue
					}

					var metum model.ProductMetum
					metum.ProductID = id
					metum.MetaKey = strings.TrimSpace(*v.Key)
					metum.MetaValue = null.StringFrom(strings.TrimSpace(*v.Value))

					if v.Id != nil && *v.Id > 0 {
						if m, ok := oMetaMap[*v.Id]; ok { // 有值且是有效值，为修改，否则为新增
							nmidMap[*v.Id] = struct{}{}

							if m.MetaValue.Valid && m.MetaKey == metum.MetaKey && m.MetaValue.String == metum.MetaValue.String { // 不变，跳过
								continue
							}

							metum.MetaID = *v.Id
						}
					}

					rel.Meta = append(rel.Meta, &metum)
				}

				for _, mid := range omids {
					if _, ok := nmidMap[mid]; !ok { // 当前不存在的值，应该删除
						rel.DelMetaIds = append(rel.DelMetaIds, mid)
					}
				}
			}
		} else { // 不存在原值，则都是新增
			for _, v := range in.Meta {
				if v.Key == nil || len(strings.TrimSpace(*v.Key)) == 0 {
					continue
				}

				if v.Value == nil || len(strings.TrimSpace(*v.Value)) == 0 {
					continue
				}

				var metum model.ProductMetum
				metum.ProductID = id
				metum.MetaKey = strings.TrimSpace(*v.Key)
				metum.MetaValue = null.StringFrom(strings.TrimSpace(*v.Value))

				rel.Meta = append(rel.Meta, &metum)
			}
		}
	}
	if len(sticky) > 0 {
		rel.Meta = append(rel.Meta, &model.ProductMetum{
			MetaID:    stickyMetaId,
			MetaKey:   "_sticky",
			MetaValue: null.StringFrom(sticky),
		})
	}
	if pendingStatus > 0 {
		rel.Meta = append(rel.Meta, &model.ProductMetum{
			MetaID:    pendingStatusMetaId,
			MetaKey:   "_pending_status",
			MetaValue: null.StringFrom(strconv.FormatInt(int64(pendingStatus), 10)),
		})
	}
	if in.SellPoint != nil && len(strings.TrimSpace(*in.SellPoint)) > 0 {
		rel.Meta = append(rel.Meta, &model.ProductMetum{
			MetaID:    sellPointMetaId,
			MetaKey:   "_sell_point",
			MetaValue: null.StringFrom(strings.TrimSpace(*in.SellPoint)),
		})
	}
	if in.StockUnit != nil && len(strings.TrimSpace(*in.StockUnit)) > 0 {
		rel.Meta = append(rel.Meta, &model.ProductMetum{
			MetaID:    stockUnitMetaId,
			MetaKey:   "_stock_unit",
			MetaValue: null.StringFrom(strings.TrimSpace(*in.StockUnit)),
		})
	}
	if in.ShowStock != nil {
		showStock := "0"
		if *in.ShowStock > 0 {
			showStock = "1"
		}
		rel.Meta = append(rel.Meta, &model.ProductMetum{
			MetaID:    showStockMetaId,
			MetaKey:   "_show_stock",
			MetaValue: null.StringFrom(showStock),
		})
	}

	if !skipMedia {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductAttachmentRelationshipColumns.ObjectID] = map[string][]any{
			"equalTo": {id},
		}
		filterMap[model.ProductAttachmentRelationshipColumns.ObjectType] = map[string][]any{
			"equalTo": {globalkey.AttachmentLogTypeProduct},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetMedia(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		mediaUidMap := make(map[uint64]*model.ProductAttachmentRelationship)
		if len(list) > 0 {
			for _, v := range list {
				mediaUidMap[v.AttachmentRelationshipID] = v
			}
		}

		allMediaMap := make(map[string]string)
		exclusiveMap := make(map[string]string) // 防删除原有图文中的图片
		if len(in.AllMedia) > 0 {
			for _, v := range in.AllMedia {
				if v.Id != nil && v.Url != nil && len(*v.Id) > 0 && len(*v.Url) > 0 {
					allMediaMap[*v.Id] = *v.Url
					exclusiveMap[*v.Url] = *v.Id
				}
			}
		}

		if len(in.NewMedia) > 0 {
			var thumbnailOrder uint
			var thumbnailId uint64
			var thumbnailUrl string
			for idx, v := range in.NewMedia {
				var url string
				if v.Url != nil && len(*v.Url) > 0 { // v.Uri  content版本（包含?id）  v.Url  url当前版本 | attachment当前版本
					url = *v.Url
				}
				var uri string
				if v.Uri != nil && len(*v.Uri) > 0 {
					uri = *v.Uri
					if len(url) == 0 {
						url = uri
					}
				}
				if len(url) == 0 {
					continue
				}

				var thumbnail uint8
				if v.Thumbnail != nil && *v.Thumbnail > 0 {
					thumbnail = uint8(*v.Thumbnail)
				}
				var order uint
				if v.Order != nil {
					order = uint(*v.Order)
				} else {
					order = uint(idx) + globalkey.AttachmentOrderOffset
				}
				var altText string
				if v.AltText != nil && len(*v.AltText) > 0 {
					altText = *v.AltText
				}
				var meta []string
				if v.Name != nil && len(*v.Name) > 0 {
					meta = append(meta, "filename:"+*v.Name)
					if len(altText) == 0 {
						altText = *v.Name
					}
				}
				if v.MetaData != nil && len(*v.MetaData) > 0 {
					meta = append(meta, *v.MetaData)
				}
				if v.MimeType != nil && len(*v.MimeType) > 0 {
					meta = append(meta, "mimeType:"+*v.MimeType)
				}
				metaData := strings.Join(meta, ";")

				var mediaId uint64
				if v.Id != nil && len(*v.Id) > 0 {
					if i, err := strconv.ParseInt(*v.Id, 10, 64); err == nil {
						mediaId = uint64(i)
					}
				}
				var uid uint64
				if v.Uid != nil && *v.Uid > 0 {
					uid = *v.Uid
				}

				if uid > 0 { // 原有缩略图
					if _, ok := mediaUidMap[uid]; ok { // 存在，修改，否则无效
						delete(mediaUidMap, uid)
						rel.Media = append(rel.Media, &model.ProductAttachmentRelationship{
							AttachmentRelationshipID: uid,
							AttachmentID:             mediaId,
							AttachmentThumbnail:      thumbnail,
							AttachmentOrder:          order,
							AttachmentImageURL:       url,
							AttachmentImageAlt:       altText,
							AttachmentImageMeta:      metaData,
						})
					}
					continue
				}

				media := &model.ProductAttachmentRelationship{
					AttachmentID:        mediaId,
					AttachmentThumbnail: thumbnail,
					AttachmentOrder:     order,
					AttachmentImageURL:  url,
					AttachmentImageAlt:  altText,
					AttachmentImageMeta: metaData,
				}

				if thumbnail == 0 && mediaId > 0 { // thumbnail == 0 不是缩略图， mediaId > 0 不是外链，获取图文uri
					if uri, ok := allMediaMap[*v.Id]; ok {
						media.AttachmentSource = uri
					}
				}

				rel.Media = append(rel.Media, media)
			}

			for _, v := range rel.Media {
				if v.AttachmentThumbnail > 0 && (len(thumbnailUrl) == 0 || thumbnailOrder > v.AttachmentOrder) { // thumbnailOrder尚未赋值，或当前值更小
					thumbnailOrder = v.AttachmentOrder
					thumbnailId = v.AttachmentID
					thumbnailUrl = v.AttachmentImageURL
				}
			}

			if len(thumbnailUrl) > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    thumbnailIdMetaId,
					MetaKey:   "_thumbnail_id",
					MetaValue: null.StringFrom(strconv.FormatInt(int64(thumbnailId), 10)),
				})
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    thumbnailUrlMetaId,
					MetaKey:   "_thumbnail_url",
					MetaValue: null.StringFrom(thumbnailUrl),
				})
			}

			rel.Meta = append(rel.Meta, &model.ProductMetum{
				MetaID:    photoCountMetaId,
				MetaKey:   "_photo_count",
				MetaValue: null.StringFrom(strconv.FormatInt(int64(len(rel.Media)), 10)),
			})
		}

		for _, v := range mediaUidMap {
			if _, ok := exclusiveMap[v.AttachmentSource]; ok { // 如果在图文中，保留一次
				delete(exclusiveMap, v.AttachmentSource)
				continue
			}

			rel.DelMediaIds = append(rel.DelMediaIds, v.AttachmentRelationshipID)
		}
	}

	if !skipCategory || !skipTag {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductTermRelationshipColumns.ObjectID] = map[string][]any{
			"equalTo": {id},
		}
		filterMap[model.ProductTermRelationshipColumns.ObjectType] = map[string][]any{
			"equalTo": {globalkey.PostTypeProduct},
		}
		list, _, err := l.svcCtx.CurrentProductTerm.GetRelationshipList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 { // 原有的 Tags（决定现有的是增还是改）
			var tids []any
			oridMap := make(map[uint64]uint64)
			for _, v := range list {
				tids = append(tids, v.TermID)
				oridMap[v.TermID] = v.TermRelationshipID
			}

			termFilterMap := make(map[string]map[string][]any)
			termFilterMap[model.ProductTermColumns.TermID] = map[string][]any{
				"in": tids,
			}
			terms, _, err := l.svcCtx.CurrentProductTerm.GetList(l.ctx, model.M{}, -1, termFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			ocidMap := make(map[uint64]struct{})
			otidMap := make(map[uint64]struct{})
			if len(terms) > 0 {
				for _, v := range terms {
					switch int32(v.Taxonomy) {
					case globalkey.TermTaxonomyCategory: // 是分类
						ocidMap[v.TermID] = struct{}{}
					case globalkey.TermTaxonomyProductTag: // 是标签
						otidMap[v.TermID] = struct{}{}
					}
				}
			}

			ntidMap := make(map[uint64]struct{})
			// 现有的 CategoryIds（决定原有的是删还是留）
			if !skipCategory && len(in.CategoryIds) > 0 {
				for _, tid := range in.CategoryIds {
					ntidMap[tid] = struct{}{}

					if _, ok := ocidMap[tid]; ok { // 已有，不新增
						continue
					}

					rel.CategoryIds = append(rel.CategoryIds, tid)
				}
			}

			// 现有的 TagIds（决定原有的是删还是留）
			if !skipTag && len(in.TagIds) > 0 {
				for _, tid := range in.TagIds {
					ntidMap[tid] = struct{}{}

					if _, ok := otidMap[tid]; ok { // 已有，不新增
						continue
					}

					rel.TagIds = append(rel.TagIds, tid)
				}
			}

			if !skipCategory {
				for cid := range ocidMap {
					if _, ok := ntidMap[cid]; !ok { // 当前不存在的值，应该删除
						if rid, ok := oridMap[cid]; ok {
							rel.DelTermIds = append(rel.DelTermIds, rid)
						}
					}
				}
			}
			if !skipTag {
				for tid := range otidMap {
					if _, ok := ntidMap[tid]; !ok { // 当前不存在的值，应该删除
						if rid, ok := oridMap[tid]; ok {
							rel.DelTermIds = append(rel.DelTermIds, rid)
						}
					}
				}
			}
		} else {
			if len(in.CategoryIds) > 0 { // 不存在原值，则都是新增
				rel.CategoryIds = in.CategoryIds
			}
			if len(in.TagIds) > 0 { // 不存在原值，则都是新增
				rel.TagIds = in.TagIds
			}
		}
	}

	if !skipEntry && in.RelationIds != nil && len(strings.TrimSpace(*in.RelationIds)) > 0 {
		relation := l.ParseRelationIds(strings.TrimSpace(*in.RelationIds))
		if relation.ToEntryID > 0 || relation.ToEntryGroupID > 0 {
			filterMap := make(map[string]map[string][]any)
			filterMap[model.ProductKnowledgeBaseRelationshipColumns.ProductID] = map[string][]any{
				"equalTo": {id},
			}
			list, _, err := l.svcCtx.CurrentProduct.GetEntryList(l.ctx, model.M{
				"pageSize": uint64(1),
				"orderBy":  model.ProductKnowledgeBaseRelationshipColumns.ProductRelationshipID + " DESC",
			}, -1, filterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				relationship := list[0]
				if relation.ToEntryID > 0 {
					relationship.ToEntryID = relation.ToEntryID
					relationship.ToEntryName = relation.ToEntryName
					relationship.ToEntrySlug = relation.ToEntrySlug
				}
				if relation.ToEntryGroupID > 0 {
					relationship.ToEntryGroupID = relation.ToEntryGroupID
					relationship.ToEntryGroupName = relation.ToEntryGroupName
					relationship.ToEntryGroupSlug = relation.ToEntryGroupSlug
				}
				rel.RelationShips = []*model.ProductKnowledgeBaseRelationship{relationship}
			} else {
				rel.RelationShips = []*model.ProductKnowledgeBaseRelationship{relation}
			}
		}
	}

	if !skipAttr && len(in.Attrs) > 0 {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductAttributeColumns.ProductID] = map[string][]any{
			"equalTo": {id},
		}
		filterMap[model.ProductAttributeColumns.IsVariationAttribute] = map[string][]any{
			"equalTo": {0},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetAttributeList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 { // 原有的 ProductAttribute（决定现有的是增还是改）
			var oaids []uint64
			var aids []any
			oaidMap := make(map[uint64]struct{})
			for _, v := range list {
				oaids = append(oaids, v.ProductAttributeID)
				aids = append(aids, v.ProductAttributeID)
				oaidMap[v.ProductAttributeID] = struct{}{}
			}

			optionFilterMap := make(map[string]map[string][]any)
			optionFilterMap[model.ProductAttributeOptionColumns.AttributeID] = map[string][]any{
				"in": aids,
			}
			optionList, _, err := l.svcCtx.CurrentProduct.GetAttributeOptionList(l.ctx, model.M{}, -1, optionFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			var oids []uint64
			oidMap := make(map[uint64]struct{})
			if len(optionList) > 0 {
				for _, v := range optionList {
					oids = append(oids, v.OptionID)
					oidMap[v.OptionID] = struct{}{}
				}
			}

			// 现有的 ProductAttribute（决定原有的是删还是留）
			naidMap := make(map[uint64]struct{})
			noidMap := make(map[uint64]struct{})
			for _, attr := range in.Attrs {
				var hasImage uint8
				if attr.HasMedia != nil && *attr.HasMedia > 0 {
					hasImage = 1
				}
				var options []*model.ProductAttributeOption
				if len(attr.Options) > 0 {
					for _, opt := range attr.Options {
						option := &model.ProductAttributeOption{
							Status: int8(globalkey.StatusEnabled),
						}
						if opt.Id != nil {
							option.TaxonomyOptionID = *opt.Id // attribute_option id
						}
						if opt.Label != nil && len(strings.TrimSpace(*opt.Label)) > 0 {
							option.Name = strings.TrimSpace(*opt.Label)
						}
						if opt.Slug != nil && len(strings.TrimSpace(*opt.Slug)) > 0 {
							option.Slug = strings.TrimSpace(*opt.Slug)
						}
						if opt.MediaId != nil {
							if i, err := strconv.ParseInt(*opt.MediaId, 10, 64); err == nil {
								option.ThumbnailID = uint64(i)
							}
						}
						if opt.MediaUrl != nil && len(strings.TrimSpace(*opt.MediaUrl)) > 0 {
							option.ThumbnailURL = strings.TrimSpace(*opt.MediaUrl)
						}
						if opt.Uid != nil && *opt.Uid > 0 {
							if _, ok := oidMap[*opt.Uid]; ok { // 有值且是有效值，为修改，否则为新增
								noidMap[*opt.Uid] = struct{}{}
								option.OptionID = *opt.Uid
							}
						}
						options = append(options, option)
					}
				}
				attribute := &model.ProductAttribute{
					ProductID: id,
					HasImage:  hasImage,
				}
				if attr.TermId != nil {
					attribute.TermID = *attr.TermId
				}
				if attr.Id != nil {
					attribute.Taxonomy = *attr.Id // attribute id
				}
				if attr.Label != nil && len(strings.TrimSpace(*attr.Label)) > 0 {
					attribute.Name = strings.TrimSpace(*attr.Label)
				}
				if attr.Slug != nil && len(strings.TrimSpace(*attr.Slug)) > 0 {
					attribute.Slug = strings.TrimSpace(*attr.Slug)
				}

				if attr.Uid != nil && *attr.Uid > 0 {
					if _, ok := oaidMap[*attr.Uid]; ok { // 有值且是有效值，为修改，否则为新增
						naidMap[*attr.Uid] = struct{}{}
						attribute.ProductAttributeID = *attr.Uid
					}
				}

				rel.Attributes = append(rel.Attributes, &model.AttributeAndOptions{
					Attribute: attribute,
					Options:   options,
				})
			}

			for _, aid := range oaids {
				if _, ok := naidMap[aid]; !ok { // 当前不存在的值，应该删除
					rel.DelAttributeIds = append(rel.DelAttributeIds, aid)
				}
			}
			for _, oid := range oids {
				if _, ok := noidMap[oid]; !ok { // 当前不存在的值，应该删除
					rel.DelAttrOptIds = append(rel.DelAttrOptIds, oid)
				}
			}
		} else { // 不存在原值，则都是新增
			for _, attr := range in.Attrs {
				var hasImage uint8
				if attr.HasMedia != nil && *attr.HasMedia > 0 {
					hasImage = 1
				}
				var options []*model.ProductAttributeOption
				if len(attr.Options) > 0 {
					for _, opt := range attr.Options {
						option := &model.ProductAttributeOption{
							Status: int8(globalkey.StatusEnabled),
						}
						if opt.Id != nil {
							option.TaxonomyOptionID = *opt.Id // attribute_option id
						}
						if opt.Label != nil && len(strings.TrimSpace(*opt.Label)) > 0 {
							option.Name = strings.TrimSpace(*opt.Label)
						}
						if opt.Slug != nil && len(strings.TrimSpace(*opt.Slug)) > 0 {
							option.Slug = strings.TrimSpace(*opt.Slug)
						}
						if opt.MediaId != nil {
							if i, err := strconv.ParseInt(*opt.MediaId, 10, 64); err == nil {
								option.ThumbnailID = uint64(i)
							}
						}
						if opt.MediaUrl != nil && len(strings.TrimSpace(*opt.MediaUrl)) > 0 {
							option.ThumbnailURL = strings.TrimSpace(*opt.MediaUrl)
						}
						options = append(options, option)
					}
				}
				attribute := &model.ProductAttribute{
					HasImage: hasImage,
				}
				if attr.TermId != nil {
					attribute.TermID = *attr.TermId
				}
				if attr.Id != nil {
					attribute.Taxonomy = *attr.Id // attribute id
				}
				if attr.Label != nil && len(strings.TrimSpace(*attr.Label)) > 0 {
					attribute.Name = strings.TrimSpace(*attr.Label)
				}
				if attr.Slug != nil && len(strings.TrimSpace(*attr.Slug)) > 0 {
					attribute.Slug = strings.TrimSpace(*attr.Slug)
				}
				rel.Attributes = append(rel.Attributes, &model.AttributeAndOptions{
					Attribute: attribute,
					Options:   options,
				})
			}
		}
	}

	rel.OptionKeyMap = make(map[int8]string)
	if !skipSpec && len(in.Specs) > 0 {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductAttributeColumns.ProductID] = map[string][]any{
			"equalTo": {id},
		}
		filterMap[model.ProductAttributeColumns.IsVariationAttribute] = map[string][]any{
			"equalTo": {1},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetAttributeList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 { // 原有的 ProductAttribute（决定现有的是增还是改）
			var oaids []uint64
			var aids []any
			oaidMap := make(map[uint64]struct{})
			for _, v := range list {
				oaids = append(oaids, v.ProductAttributeID)
				aids = append(aids, v.ProductAttributeID)
				oaidMap[v.ProductAttributeID] = struct{}{}
			}

			optionFilterMap := make(map[string]map[string][]any)
			optionFilterMap[model.ProductAttributeOptionColumns.AttributeID] = map[string][]any{
				"in": aids,
			}
			optionList, _, err := l.svcCtx.CurrentProduct.GetAttributeOptionList(l.ctx, model.M{}, -1, optionFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			var oids []uint64
			oidMap := make(map[uint64]struct{})
			if len(optionList) > 0 {
				for _, v := range optionList {
					oids = append(oids, v.OptionID)
					oidMap[v.OptionID] = struct{}{}
				}
			}

			// 现有的 ProductAttribute（决定原有的是删还是留）
			naidMap := make(map[uint64]struct{})
			noidMap := make(map[uint64]struct{})
			var tmpIdx int8 = -1
			for _, spec := range in.Specs {
				var hasImage uint8
				if spec.HasMedia != nil && *spec.HasMedia > 0 {
					hasImage = 1
				}
				var options []*model.ProductAttributeOption
				if len(spec.Options) > 0 {
					for _, opt := range spec.Options {
						tmpIdx -= 1
						option := &model.ProductAttributeOption{
							Status: tmpIdx,
						}
						if opt.Key != nil {
							rel.OptionKeyMap[tmpIdx] = *opt.Key
						}
						if opt.Id != nil {
							option.TaxonomyOptionID = *opt.Id // attribute_option id
						}
						if opt.Label != nil && len(strings.TrimSpace(*opt.Label)) > 0 {
							option.Name = strings.TrimSpace(*opt.Label)
						}
						if opt.Slug != nil && len(strings.TrimSpace(*opt.Slug)) > 0 {
							option.Slug = strings.TrimSpace(*opt.Slug)
						}
						if opt.MediaId != nil {
							if i, err := strconv.ParseInt(*opt.MediaId, 10, 64); err == nil {
								option.ThumbnailID = uint64(i)
							}
						}
						if opt.MediaUrl != nil && len(strings.TrimSpace(*opt.MediaUrl)) > 0 {
							option.ThumbnailURL = strings.TrimSpace(*opt.MediaUrl)
						}
						if opt.Uid != nil && *opt.Uid > 0 {
							if _, ok := oidMap[*opt.Uid]; ok { // 有值且是有效值，为修改，否则为新增
								noidMap[*opt.Uid] = struct{}{}
								option.OptionID = *opt.Uid
							}
						}
						options = append(options, option)
					}
				}
				attribute := &model.ProductAttribute{
					ProductID:            id,
					HasImage:             hasImage,
					IsVariationAttribute: 1,
				}
				if spec.TermId != nil {
					attribute.TermID = *spec.TermId
				}
				if spec.Id != nil {
					attribute.Taxonomy = *spec.Id // attribute id
				}
				if spec.Label != nil && len(strings.TrimSpace(*spec.Label)) > 0 {
					attribute.Name = strings.TrimSpace(*spec.Label)
				}
				if spec.Slug != nil && len(strings.TrimSpace(*spec.Slug)) > 0 {
					attribute.Slug = strings.TrimSpace(*spec.Slug)
				}

				if spec.Uid != nil && *spec.Uid > 0 {
					if _, ok := oaidMap[*spec.Uid]; ok { // 有值且是有效值，为修改，否则为新增
						naidMap[*spec.Uid] = struct{}{}
						attribute.ProductAttributeID = *spec.Uid
					}
				}

				rel.Attributes = append(rel.Attributes, &model.AttributeAndOptions{
					Attribute: attribute,
					Options:   options,
				})
			}

			for _, aid := range oaids {
				if _, ok := naidMap[aid]; !ok { // 当前不存在的值，应该删除
					rel.DelAttributeIds = append(rel.DelAttributeIds, aid)
				}
			}
			for _, oid := range oids {
				if _, ok := noidMap[oid]; !ok { // 当前不存在的值，应该删除
					rel.DelAttrOptIds = append(rel.DelAttrOptIds, oid)
				}
			}
		} else {
			var tmpIdx int8 = -1
			for _, spec := range in.Specs {
				var hasImage uint8
				if spec.HasMedia != nil && *spec.HasMedia > 0 {
					hasImage = 1
				}
				var options []*model.ProductAttributeOption
				if len(spec.Options) > 0 {
					for _, opt := range spec.Options {
						tmpIdx -= 1
						option := &model.ProductAttributeOption{
							Status: tmpIdx,
						}
						if opt.Key != nil {
							rel.OptionKeyMap[tmpIdx] = *opt.Key
						}
						if opt.Id != nil {
							option.TaxonomyOptionID = *opt.Id // attribute_option id
						}
						if opt.Label != nil && len(strings.TrimSpace(*opt.Label)) > 0 {
							option.Name = strings.TrimSpace(*opt.Label)
						}
						if opt.Slug != nil && len(strings.TrimSpace(*opt.Slug)) > 0 {
							option.Slug = strings.TrimSpace(*opt.Slug)
						}
						if opt.MediaId != nil {
							if i, err := strconv.ParseInt(*opt.MediaId, 10, 64); err == nil {
								option.ThumbnailID = uint64(i)
							}
						}
						if opt.MediaUrl != nil && len(strings.TrimSpace(*opt.MediaUrl)) > 0 {
							option.ThumbnailURL = strings.TrimSpace(*opt.MediaUrl)
						}
						options = append(options, option)
					}
				}
				attribute := &model.ProductAttribute{
					HasImage:             hasImage,
					IsVariationAttribute: 1,
				}
				if spec.TermId != nil {
					attribute.TermID = *spec.TermId
				}
				if spec.Id != nil {
					attribute.Taxonomy = *spec.Id // attribute id
				}
				if spec.Label != nil && len(strings.TrimSpace(*spec.Label)) > 0 {
					attribute.Name = strings.TrimSpace(*spec.Label)
				}
				if spec.Slug != nil && len(strings.TrimSpace(*spec.Slug)) > 0 {
					attribute.Slug = strings.TrimSpace(*spec.Slug)
				}
				rel.Attributes = append(rel.Attributes, &model.AttributeAndOptions{
					Attribute: attribute,
					Options:   options,
				})
			}
		}
	}

	if !skipSku && len(in.Skus) > 0 {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductVariationColumns.ProductID] = map[string][]any{
			"equalTo": {id},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetVariationList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 { // 原有的 ProductVariation（决定现有的是增还是改）
			var ovids []uint64
			var vids []any
			ovidMap := make(map[uint64]struct{})
			for _, v := range list {
				ovids = append(ovids, v.ProductVariationID)
				vids = append(vids, v.ProductVariationID)
				ovidMap[v.ProductVariationID] = struct{}{}
			}

			metaFilterMap := make(map[string]map[string][]any)
			metaFilterMap[model.ProductVariationMetumColumns.ProductID] = map[string][]any{
				"in": vids,
			}
			metaList, _, err := l.svcCtx.CurrentProduct.GetVariationMeta(l.ctx, model.M{}, -1, metaFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			var omids []uint64
			variationMetaIdMap := make(map[uint64]map[string]uint64)
			if len(metaList) > 0 {
				for _, v := range metaList {
					if _, ok := variationMetaIdMap[v.ProductID]; !ok {
						variationMetaIdMap[v.ProductID] = make(map[string]uint64)
					}
					if len(v.MetaKey) > 0 && v.MetaValue.Valid {
						omids = append(omids, v.MetaID)
						variationMetaIdMap[v.ProductID][v.MetaKey] = v.MetaID
					}
				}
			}

			// 现有的 ProductVariation（决定原有的是删还是留）
			nvidMap := make(map[uint64]struct{})
			nmidMap := make(map[uint64]struct{})
			var saleCount uint32
			var gemPriceMin int64
			var gemPriceMax int64
			var retailPriceMin int64
			var retailPriceMax int64
			for _, sku := range in.Skus {
				variation := &model.ProductVariation{}
				if sku.GemPrice != nil && len(*sku.GemPrice) > 0 {
					gemPrice, err := moneyx.ParseGem(*sku.GemPrice)
					if err != nil {
						logx.Errorw("failed to ParseGem", logx.Field("detail", err.Error()))
					} else {
						variation.GemPrice = uint64(gemPrice)
						if gemPriceMin == 0 || gemPriceMin > gemPrice {
							gemPriceMin = gemPrice
						}
						if gemPriceMax == 0 || gemPriceMax < gemPrice {
							gemPriceMax = gemPrice
						}
					}
				}
				if sku.RetailPrice != nil && len(*sku.RetailPrice) > 0 {
					retailPrice, err := moneyx.ParseAmount(*sku.RetailPrice)
					if err != nil {
						logx.Errorw("failed to ParseAmount", logx.Field("detail", err.Error()))
					} else {
						variation.RetailPrice = uint64(retailPrice)
						if retailPriceMin == 0 || retailPriceMin > retailPrice {
							retailPriceMin = retailPrice
						}
						if retailPriceMax == 0 || retailPriceMax < retailPrice {
							retailPriceMax = retailPrice
						}
					}
				}
				if sku.CounterPrice != nil && len(*sku.CounterPrice) > 0 {
					counterPrice, err := moneyx.ParseAmount(*sku.CounterPrice)
					if err != nil {
						logx.Errorw("failed to ParseAmount", logx.Field("detail", err.Error()))
					} else {
						variation.CounterPrice = uint64(counterPrice)
					}
				}
				if sku.Weight != nil && len(*sku.Weight) > 0 {
					weight, err := moneyx.ParseWeight(*sku.Weight)
					if err != nil {
						logx.Errorw("failed to ParseWeight", logx.Field("detail", err.Error()))
					} else {
						variation.Weight = uint(weight)
					}
				}
				if sku.SkuCode != nil && len(*sku.SkuCode) > 0 {
					variation.SkuCode = *sku.SkuCode
				}
				if sku.Status != nil {
					variation.Status = uint8(*sku.Status)
				}

				var variationMeta []*model.ProductVariationMetum
				var thumbnailIdVariationMetaId uint64
				var thumbnailUrlVariationMetaId uint64
				var preSaleCountVariationMetaId uint64
				var skuVariationMetaId uint64
				var skuKeyVariationMetaId uint64
				if sku.Id != nil && *sku.Id > 0 {
					if _, ok := ovidMap[*sku.Id]; ok { // 有值且是有效值，为修改，否则为新增
						nvidMap[*sku.Id] = struct{}{}
						variation.ProductVariationID = *sku.Id
					}
					if metaIdMap, ok := variationMetaIdMap[*sku.Id]; ok {
						for metaKey, metaId := range metaIdMap {
							switch metaKey { // 先排除特殊Key
							case "_thumbnail_id":
								thumbnailIdVariationMetaId = metaId
							case "_thumbnail_url":
								thumbnailUrlVariationMetaId = metaId
							case "_pre_sale_count":
								preSaleCountVariationMetaId = metaId
							case "_sku":
								skuVariationMetaId = metaId
							case "_sku_key":
								skuKeyVariationMetaId = metaId
							}
						}
					}
				}
				if sku.SaleCount != nil && *sku.SaleCount > 0 {
					variationMeta = append(variationMeta, &model.ProductVariationMetum{
						MetaID:    preSaleCountVariationMetaId,
						MetaKey:   "_pre_sale_count",
						MetaValue: null.StringFrom(strconv.FormatInt(int64(*sku.SaleCount), 10)),
					})
					nmidMap[preSaleCountVariationMetaId] = struct{}{}
					saleCount += *sku.SaleCount
				}
				if sku.MediaId != nil && len(*sku.MediaId) > 0 {
					variationMeta = append(variationMeta, &model.ProductVariationMetum{
						MetaID:    thumbnailIdVariationMetaId,
						MetaKey:   "_thumbnail_id",
						MetaValue: null.StringFrom(*sku.MediaId),
					})
					nmidMap[thumbnailIdVariationMetaId] = struct{}{}
				}
				if sku.MediaUrl != nil && len(strings.TrimSpace(*sku.MediaUrl)) > 0 {
					variationMeta = append(variationMeta, &model.ProductVariationMetum{
						MetaID:    thumbnailUrlVariationMetaId,
						MetaKey:   "_thumbnail_url",
						MetaValue: null.StringFrom(*sku.MediaUrl),
					})
					nmidMap[thumbnailUrlVariationMetaId] = struct{}{}
				}
				if len(sku.Specs) > 0 {
					skuStr := l.FormatSpecs(sku.Specs)
					if len(skuStr) > 0 && sku.Key != nil && len(strings.TrimSpace(*sku.Key)) > 0 {
						variationMeta = append(variationMeta, &model.ProductVariationMetum{
							MetaID:    skuVariationMetaId,
							MetaKey:   "_sku",
							MetaValue: null.StringFrom(skuStr),
						})
						variationMeta = append(variationMeta, &model.ProductVariationMetum{
							MetaID:    skuKeyVariationMetaId,
							MetaKey:   "_sku_key",
							MetaValue: null.StringFrom(strings.TrimSpace(*sku.Key)),
						})
						nmidMap[skuVariationMetaId] = struct{}{}
						nmidMap[skuKeyVariationMetaId] = struct{}{}
					}
				}

				rel.Variations = append(rel.Variations, &model.VariationAndMeta{
					Variation: variation,
					Meta:      variationMeta,
				})
			}
			for _, vid := range ovids {
				if _, ok := nvidMap[vid]; !ok { // 当前不存在的值，应该删除
					rel.DelVariationIds = append(rel.DelVariationIds, vid)
				}
			}
			for _, mid := range omids {
				if _, ok := nmidMap[mid]; !ok { // 当前不存在的值，应该删除
					rel.DelVariationMetaIds = append(rel.DelVariationMetaIds, mid)
				}
			}
			if saleCount > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    preSaleCountMetaId,
					MetaKey:   "_pre_sale_count",
					MetaValue: null.StringFrom(strconv.FormatInt(int64(saleCount), 10)),
				})
			}
			if gemPriceMin > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    gemPriceMinMetaId,
					MetaKey:   "_gem_price_min",
					MetaValue: null.StringFrom(moneyx.FormatGem(gemPriceMin)),
				})
			}
			if gemPriceMax > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    gemPriceMaxMetaId,
					MetaKey:   "_gem_price_max",
					MetaValue: null.StringFrom(moneyx.FormatGem(gemPriceMax)),
				})
			}
			if retailPriceMin > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    retailPriceMinMetaId,
					MetaKey:   "_retail_price_min",
					MetaValue: null.StringFrom(moneyx.FormatAmount(retailPriceMin)),
				})
			}
			if retailPriceMax > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    retailPriceMaxMetaId,
					MetaKey:   "_retail_price_max",
					MetaValue: null.StringFrom(moneyx.FormatAmount(retailPriceMax)),
				})
			}
		} else {
			var saleCount uint32
			var gemPriceMin int64
			var gemPriceMax int64
			var retailPriceMin int64
			var retailPriceMax int64
			for _, sku := range in.Skus {
				variation := &model.ProductVariation{}
				if sku.GemPrice != nil && len(*sku.GemPrice) > 0 {
					gemPrice, err := moneyx.ParseGem(*sku.GemPrice)
					if err != nil {
						logx.Errorw("failed to ParseGem", logx.Field("detail", err.Error()))
					} else {
						variation.GemPrice = uint64(gemPrice)
						if gemPriceMin == 0 || gemPriceMin > gemPrice {
							gemPriceMin = gemPrice
						}
						if gemPriceMax == 0 || gemPriceMax < gemPrice {
							gemPriceMax = gemPrice
						}
					}
				}
				if sku.RetailPrice != nil && len(*sku.RetailPrice) > 0 {
					retailPrice, err := moneyx.ParseAmount(*sku.RetailPrice)
					if err != nil {
						logx.Errorw("failed to ParseAmount", logx.Field("detail", err.Error()))
					} else {
						variation.RetailPrice = uint64(retailPrice)
						if retailPriceMin == 0 || retailPriceMin > retailPrice {
							retailPriceMin = retailPrice
						}
						if retailPriceMax == 0 || retailPriceMax < retailPrice {
							retailPriceMax = retailPrice
						}
					}
				}
				if sku.CounterPrice != nil && len(*sku.CounterPrice) > 0 {
					counterPrice, err := moneyx.ParseAmount(*sku.CounterPrice)
					if err != nil {
						logx.Errorw("failed to ParseAmount", logx.Field("detail", err.Error()))
					} else {
						variation.CounterPrice = uint64(counterPrice)
					}
				}
				if sku.Weight != nil && len(*sku.Weight) > 0 {
					weight, err := moneyx.ParseWeight(*sku.Weight)
					if err != nil {
						logx.Errorw("failed to ParseWeight", logx.Field("detail", err.Error()))
					} else {
						variation.Weight = uint(weight)
					}
				}
				if sku.SkuCode != nil && len(*sku.SkuCode) > 0 {
					variation.SkuCode = *sku.SkuCode
				}
				if sku.Status != nil {
					variation.Status = uint8(*sku.Status)
				}
				var variationMeta []*model.ProductVariationMetum
				if sku.SaleCount != nil && *sku.SaleCount > 0 {
					variationMeta = append(variationMeta, &model.ProductVariationMetum{
						MetaKey:   "_pre_sale_count",
						MetaValue: null.StringFrom(strconv.FormatInt(int64(*sku.SaleCount), 10)),
					})
					saleCount += *sku.SaleCount
				}
				if sku.MediaId != nil && len(*sku.MediaId) > 0 {
					variationMeta = append(variationMeta, &model.ProductVariationMetum{
						MetaKey:   "_thumbnail_id",
						MetaValue: null.StringFrom(*sku.MediaId),
					})
				}
				if sku.MediaUrl != nil && len(strings.TrimSpace(*sku.MediaUrl)) > 0 {
					variationMeta = append(variationMeta, &model.ProductVariationMetum{
						MetaKey:   "_thumbnail_url",
						MetaValue: null.StringFrom(*sku.MediaUrl),
					})
				}
				if len(sku.Specs) > 0 {
					skuStr := l.FormatSpecs(sku.Specs)
					if len(skuStr) > 0 && sku.Key != nil && len(strings.TrimSpace(*sku.Key)) > 0 {
						variationMeta = append(variationMeta, &model.ProductVariationMetum{
							MetaKey:   "_sku",
							MetaValue: null.StringFrom(skuStr),
						})
						variationMeta = append(variationMeta, &model.ProductVariationMetum{
							MetaKey:   "_sku_key",
							MetaValue: null.StringFrom(strings.TrimSpace(*sku.Key)),
						})
					}
				}
				rel.Variations = append(rel.Variations, &model.VariationAndMeta{
					Variation: variation,
					Meta:      variationMeta,
				})
			}
			if saleCount > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    preSaleCountMetaId,
					MetaKey:   "_pre_sale_count",
					MetaValue: null.StringFrom(strconv.FormatInt(int64(saleCount), 10)),
				})
			}
			if gemPriceMin > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    gemPriceMinMetaId,
					MetaKey:   "_gem_price_min",
					MetaValue: null.StringFrom(moneyx.FormatGem(gemPriceMin)),
				})
			}
			if gemPriceMax > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    gemPriceMaxMetaId,
					MetaKey:   "_gem_price_max",
					MetaValue: null.StringFrom(moneyx.FormatGem(gemPriceMax)),
				})
			}
			if retailPriceMin > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    retailPriceMinMetaId,
					MetaKey:   "_retail_price_min",
					MetaValue: null.StringFrom(moneyx.FormatAmount(retailPriceMin)),
				})
			}
			if retailPriceMax > 0 {
				rel.Meta = append(rel.Meta, &model.ProductMetum{
					MetaID:    retailPriceMaxMetaId,
					MetaKey:   "_retail_price_max",
					MetaValue: null.StringFrom(moneyx.FormatAmount(retailPriceMax)),
				})
			}
		}
	}

	err = l.svcCtx.CurrentProduct.Update(l.ctx, tx, goods, rel)
	if err != nil {
		tx.Rollback()
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	tx.Commit()

	return &product.BaseResp{
		Msg: i18n.UpdateSuccess,
	}, nil
}

func (l *UpdateProductLogic) ParseRelationIds(relationIds string) *model.ProductKnowledgeBaseRelationship {
	relationship := &model.ProductKnowledgeBaseRelationship{}
	pairs := strings.Split(relationIds, arrayKeyValueSeparator)
	if len(pairs) > 0 {
		for _, pair := range pairs {
			parts := strings.Split(pair, keyValueSeparator)
			if len(parts) == 2 && len(parts[0]) > 0 && len(parts[1]) > 0 {
				if parts[0] == "perfumes" {
					values := strings.Split(parts[1], complexLSVSeparator)
					if len(values) == 3 && len(values[0]) > 0 {
						if i, err := strconv.ParseInt(values[0], 10, 64); err == nil {
							relationship.ToEntryID = uint64(i)
							relationship.ToEntryName = values[1]
							relationship.ToEntrySlug = values[2]
						}
					}
				} else if parts[0] == "brands" {
					values := strings.SplitN(parts[1], complexLSVSeparator, 3)
					if len(values) == 3 && len(values[0]) > 0 {
						if i, err := strconv.ParseInt(values[0], 10, 64); err == nil {
							relationship.ToEntryGroupID = uint64(i)
							relationship.ToEntryGroupName = values[1]
							relationship.ToEntryGroupSlug = values[2]
						}
					}
				}
			}
		}
	}

	return relationship
}

func (l *UpdateProductLogic) FormatSpecs(specs []*product.ProductAttrInfo) (res string) {
	if len(specs) > 0 {
		var items []string
		for _, spec := range specs {
			var opts []string
			if len(spec.Options) > 0 {
				for _, opt := range spec.Options {
					if opt.Id != nil && opt.Label != nil {
						var slug string
						if opt.Slug != nil {
							slug = *opt.Slug
						}
						opts = append(opts, fmt.Sprintf("%d%s%s%s%s", *opt.Id, LSVSeparator, *opt.Label, LSVSeparator, slug))
					}
				}
			}
			if spec.Id != nil && spec.Label != nil {
				var slug string
				if spec.Slug != nil {
					slug = *spec.Slug
				}
				items = append(
					items,
					fmt.Sprintf("%d%s%s%s%s%s%s", *spec.Id, LSVSeparator, *spec.Label, LSVSeparator, slug, keyValueSeparator, strings.Join(opts, arrayValueSeparator)),
				)
			}
		}
		return strings.Join(items, arrayKeyValueSeparator)
	}

	return
}
